package com.jfinal.utils.http;

import com.jfinal.kit.StrKit;
import com.jfinal.utils.MediaFile;
import com.squareup.okhttp.*;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * OkHttp代理实现
 */
public class OkHttpDelegate implements HttpDelegate {
    private static final OkHttpClient httpClient = new OkHttpClient();
    private static final OkHttpClient httpsClient = httpClient.clone();

    Lock lock = new ReentrantLock();

    public static final MediaType CONTENT_TYPE_FORM = MediaType.parse("application/x-www-form-urlencoded");

    //分别设置Http的连接,写入,读取的超时时间为30秒
    static {
        httpClient.setConnectTimeout(10, TimeUnit.SECONDS);
        httpClient.setWriteTimeout(10, TimeUnit.SECONDS);
        httpClient.setReadTimeout(30, TimeUnit.SECONDS);
    }

    /**
     * 同步请求
     *
     * @param request
     * @return
     */
    private static String execute(Request request) {
        try {
            Response response = httpClient.newCall(request).execute();
            if (!response.isSuccessful()) {
                throw new RuntimeException("Unexpected code " + response);
            }
            return response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 异步请求
     *
     * @param request
     * @param responseCallback
     */
    private static void enqueue(Request request, Callback responseCallback) {
        httpClient.newCall(request).enqueue(responseCallback);
    }

    /**
     * ======================================================
     * ==================== HTTP 简单代理实现 ====================
     * ======================================================
     */

    @Override
    public String get(String url) {
        Request request = new Request.Builder().url(url).get().build();
        return execute(request);
    }

    @Override
    public String get(String url, Map<String, String> formParas) {
        HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
        for (Map.Entry<String, String> entry : formParas.entrySet()) {
            urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
        }
        HttpUrl httpUrl = urlBuilder.build();
        Request request = new Request.Builder().url(httpUrl).get().build();
        return execute(request);
    }

    @Override
    public String post(String url) {
        Request request = new Request.Builder().url(url).post(new FormEncodingBuilder().build()).build();
        return execute(request);
    }

    @Override
    public String post(String url, String params) {
        RequestBody body = RequestBody.create(CONTENT_TYPE_FORM, params);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        return execute(request);
    }

    @Override
    public String post(String url, Map<String, String> formParas) {
        FormEncodingBuilder builder = new FormEncodingBuilder();
        for (Map.Entry<String, String> entry : formParas.entrySet()) {
            builder.add(entry.getKey(), entry.getValue());
        }
        Request request = new Request.Builder().url(url).post(builder.build()).build();
        return execute(request);
    }

    @Override
    public String postSSL(String url, String data, String certPath, String certPass) {
        RequestBody body = RequestBody.create(CONTENT_TYPE_FORM, data);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        try (FileInputStream inputStream = new FileInputStream(certPath)) {
            KeyStore clientStore = KeyStore.getInstance("PKCS12");
            clientStore.load(inputStream, certPass.toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(clientStore, certPass.toCharArray());
            KeyManager[] kms = kmf.getKeyManagers();
            SSLContext sslContext = SSLContext.getInstance("TLSv1");

            sslContext.init(kms, null, new SecureRandom());
            lock.lock();
            httpsClient.setSslSocketFactory(sslContext.getSocketFactory());

            Response response = httpsClient.newCall(request).execute();

            if (!response.isSuccessful()) throw new RuntimeException("Unexpected code " + response);

            return response.body().string();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public MediaFile download(String url) {
        Request request = new Request.Builder().url(url).get().build();
        try {
            Response response = httpsClient.newCall(request).execute();

            if (!response.isSuccessful()) throw new RuntimeException("Unexpected code " + response);

            ResponseBody body = response.body();
            MediaType mediaType = body.contentType();
            MediaFile mediaFile = new MediaFile();
            if (mediaType.type().equals("text")) {
                mediaFile.setError(body.string());
            } else {
                BufferedInputStream bis = new BufferedInputStream(body.byteStream());

                String ds = response.header("Content-disposition");
                String fullName = ds.substring(ds.indexOf("filename=\"") + 10, ds.length() - 1);
                String relName = fullName.substring(0, fullName.lastIndexOf("."));
                String suffix = fullName.substring(relName.length() + 1);

                mediaFile.setFullName(fullName);
                mediaFile.setFileName(relName);
                mediaFile.setSuffix(suffix);
                mediaFile.setContentLength(body.contentLength() + "");
                mediaFile.setContentType(body.contentType().toString());
                mediaFile.setFileStream(bis);
            }
            return mediaFile;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public InputStream download(String url, String params) {
        Request request;
        if (StrKit.notBlank(params)) {
            RequestBody body = RequestBody.create(CONTENT_TYPE_FORM, params);
            request = new Request.Builder().url(url).post(body).build();
        } else {
            request = new Request.Builder().url(url).get().build();
        }
        try {
            Response response = httpsClient.newCall(request).execute();

            if (!response.isSuccessful()) throw new RuntimeException("Unexpected code " + response);

            return response.body().byteStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String upload(String url, File file, String params) {
        RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);

        MultipartBuilder builder = new MultipartBuilder()
                .type(MultipartBuilder.FORM)
                .addFormDataPart("media", file.getName(), fileBody);

        if (StrKit.notBlank(params)) {
            builder.addFormDataPart("description", params);
        }

        RequestBody requestBody = builder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();

        return execute(request);
    }

}
